﻿//
//创建日期： 2010-8-27
//描述： 基于LINQ To XML，对实体的增删改查操作，利用了反射特性
//最后修改日期：2010-10-31

using System;
using System.Xml.Linq;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
using System.Text;

namespace WISD.Common
{
    /// <summary>
    /// 自定义实体头属性
    /// </summary>
    public class HeaderAttribute :Attribute
    {
        /// <summary>
        /// 主键
        /// </summary>
        public string PrimaryKey { get; set; }

        /// <summary>
        /// XML文件中父节点名
        /// </summary>
        public string XmlRootName { get; set; }
    }

    /// <summary>
    /// 类
    /// </summary>
    /// <typeparam name="T">实体类型</typeparam>
    public class XmlHelper<T> where T:new()
    {
        /// <summary>
        /// XML文件与实体属性对应关系
        /// </summary>
        public enum XMLType
        {
            /// <summary>
            /// XML文件中是小写
            /// </summary>
            ToLower,

            /// <summary>
            /// XML文件中是大写
            /// </summary>
            ToUpper,

            /// <summary>
            /// XML文件中与实体属性名一致
            /// </summary>
            Normal,
        }

        /// <summary>
        /// XDocument
        /// </summary>
        private XDocument doc;

        /// <summary>
        /// XML文件与实体属性对应关系
        /// </summary>
        public XMLType XmlType
        {
            get;
            set;
        }

        /// <summary>
        /// 实体主键名
        /// </summary>
        private string primaryKey;

        /// <summary>
        /// 实体主键名
        /// </summary>
        public string PrimaryKey
        {
            get { return primaryKey; }
        }

        /// <summary>
        /// 表名，对应XML文件中实体的父节点
        /// </summary>
        private string tableName;
       
        /// <summary>
        /// 实体类型
        /// </summary>
        Type type;


        private string filePath;

        /// <summary>
        /// 实体的所有公共属性
        /// </summary>
        private List<PropertyInfo> properties;

        /// <summary>
        /// XDocument
        /// </summary>
        public XDocument Document
        {
            get { return doc; }
            set { doc = value; }
        }

        /// <summary>
        /// XML文件中包含实体节点的父节点名
        /// </summary>
        public string TableName
        {
            get { return tableName; }            
        }

        /// <summary>
        /// XDocument的字符串
        /// </summary>
        public string DocumentString
        {
            get
            {
                if (doc == null)
                {
                    throw new ArgumentNullException("doc");
                }
                StringBuilder builder = new StringBuilder();
                if (doc.Declaration != null)
                {
                    builder.Append(doc.Declaration.ToString()); //Declaration
                }
                builder.Append(doc.ToString()); //xml本体
                return builder.ToString();
            }
        }

        private static XmlHelper<T> _xmlHelper;

        /// <summary>
        /// 由具体类型初始化
        /// </summary>        
        private XmlHelper()
        {
            this.XmlType = XMLType.Normal;
            this.type = typeof(T);
            object[] attr = type.GetCustomAttributes(false);
            if (attr.Count() != 0)
            {
                HeaderAttribute header = attr[0] as HeaderAttribute;
                primaryKey = header.PrimaryKey;
                tableName = header.XmlRootName;
            }
            else
            {
                primaryKey = string.Empty;
                tableName = string.Empty;
            }           

            //所有公共属性
            properties = type.GetProperties().ToList();
        }
        
        /// <summary>
        /// 保存修改
        /// </summary>
        /// <param name="filePath"></param>
        public void SaveChanges(string filePath)
        {
            try
            {
                doc.Save(filePath);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public void SaveChanges()
        {
            try
            {
                doc.Save(this.filePath);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 加载XML
        /// </summary>
        /// <param name="xmlStr">路径或者是XML字符串</param>
        public static XmlHelper<T> Load(string xmlStr)
        {
            try
            {
                if (_xmlHelper == null)
                {
                    _xmlHelper = new XmlHelper<T>();
                }

                _xmlHelper.doc = XDocument.Parse(xmlStr);
                return _xmlHelper;
            }
            catch
            {
                try
                {
                    _xmlHelper.doc = XDocument.Load(xmlStr);
                    _xmlHelper.filePath = xmlStr;
                    return _xmlHelper;
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }            
        }
        

        /// <summary>
        /// 判断正确性
        /// </summary>
        private void Assert()
        {            
            if (doc == null)
            {
                throw new Exception("XML文件加载失败");
            }
            if (type != typeof(T))
            {
                throw new Exception("类型不匹配");
            }
        }

        /// <summary>
        /// 判断正确性
        /// </summary>
        private void Assert(Object Object)
        {
            if (doc == null)
            {
                throw new Exception("XML文件加载失败");
            }
            if (type != Object.GetType())
            {
                throw new Exception("类型不匹配");
            }
        }

        /// <summary>
        /// 是否存在
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="predicate">条件</param>
        /// <returns></returns>
        public bool Exist(Func<T, bool> predicate)
        {
            return Select(predicate).Count > 0 ? true : false;
        }

        /// <summary>
        /// 是否存在（主键是否重复）
        /// </summary>        
        /// <param name="Object">实体</param>
        /// <returns></returns>
        public bool Exist(Object Object)
        {
            Assert(Object);
            PropertyInfo info = properties.Single(s => s.Name == primaryKey);
            object tValue = info.GetValue(Object, null); //传进来的实体主键的值
            if (tValue == null) //主键为空，直接返回True
            {
                return true;
            }
            Func<XElement, bool> predicate = p =>
            {                
                object xValue = Convert.ChangeType(p.Element(GetXmlNodeName(info.Name)).Value, info.PropertyType, null); //XML文件中实体主键的值                   
                return xValue.Equals(tValue);
            };
            IEnumerable<XElement> result = doc.Descendants(GetXmlNodeName(type.Name)).Where(predicate);
            return result.Count() == 0 ? false : true;
        }

        /// <summary>
        /// 根据实体返回XElement（主键匹配），内部使用
        /// </summary>
        /// <param name="Object"></param>
        /// <returns></returns>
        private XElement GetElement(Object Object)
        {
            PropertyInfo info = properties.Single(s => s.Name == primaryKey);
            object tValue = info.GetValue(Object, null); //传进来的实体主键的值
            if (tValue == null) //主键为空，直接返回null
            {
                return null;
            }
            Func<XElement, bool> predicate = p =>
            {
                object xValue = Convert.ChangeType(p.Element(GetXmlNodeName(info.Name)).Value, info.PropertyType, null); //XML文件中实体主键的值                   
                return xValue.Equals(tValue);
            };
            try
            {
                return doc.Descendants(GetXmlNodeName(type.Name)).Single(predicate);
            }
            catch
            {
                return null;
            }            
        }

        /// <summary>
        /// 获得查询条件
        /// </summary>
        /// <returns></returns>
        public string GetQueryString()
        {
            Assert();

            Func<XElement, string> selector = x =>
            {
                StringBuilder strWhere = new StringBuilder();
                string queryInt = " {0} = {1} ";   //值为数字时，不用加单引号
                string queryString = " {0} = '{1}' "; //值为字符串等，加单引号

                //将属性值不为空的，作为查询条件
                properties.ForEach(p =>
                {
                    XElement element = x.Element(GetXmlNodeName(p.Name));

                    if (element != null)
                    {
                        string value = element.Value;
                        if (!string.IsNullOrEmpty(value))
                        {
                            if (p.PropertyType == typeof(int) ||
                                p.PropertyType == typeof(decimal) ||
                                p.PropertyType == typeof(int?) ||
                                p.PropertyType == typeof(decimal?))  //这些为数字
                            {
                                strWhere.Append(string.Format(queryInt, p.Name, value) + "and");
                            }
                            else //非数字
                            {
                                strWhere.Append(string.Format(queryString, p.Name, value) + "and");
                            }
                        }
                    }
                });

                string result = strWhere.ToString();
                if (result.Length > 0)
                {
                    result = result.Remove(result.LastIndexOf("and")); //去掉最后的"and"
                }

                return result;
            };
            return doc.Descendants(GetXmlNodeName(type.Name)).Select(selector).First();
        }
                

        /// <summary>
        /// 查询
        /// </summary>        
        public List<T> Select()
        {
            Assert();
            
            Func<XElement, T> selector = x => //选择器
            {
                T t = new T();
                properties.ForEach(p =>
                {
                    //逐个属性赋值，自动类型转换
                    try
                    {
                        p.SetValue(t, Convert.ChangeType(x.Element(GetXmlNodeName(p.Name)).Value, p.PropertyType, null), null);
                    }
                    catch //某些特殊的带？的类型无法强转
                    {
                        try
                        {
                            if (p.PropertyType == typeof(DateTime?))
                            {
                                p.SetValue(t, Convert.ToDateTime(x.Element(GetXmlNodeName(p.Name)).Value), null);
                            }
                            else if (p.PropertyType == typeof(int?))
                            {
                                p.SetValue(t, Convert.ToInt32(x.Element(GetXmlNodeName(p.Name)).Value), null);
                            }
                            else if (p.PropertyType == typeof(decimal?))
                            {
                                p.SetValue(t, Convert.ToDecimal(x.Element(GetXmlNodeName(p.Name)).Value), null);
                            }
                        }
                        catch { }
                    }
                });
                return t;
            };
            return doc.Descendants(GetXmlNodeName(type.Name)).Select(selector).ToList();
        }

        /// <summary>
        /// 按条件查询
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="predicate">条件</param>        
        public List<T> Select(Func<T, bool> predicate)
        {
            return Select().Where(predicate).ToList();
        }

        /// <summary>
        /// 插入
        /// </summary>        
        /// <param name="Object">实体</param>
        public void Insert(Object Object)
        {
            Insert(new Object[] { Object });
        }

        /// <summary>
        /// 批量插入
        /// </summary>        
        /// <param name="entities">实体列表</param>
        public void Insert(Object[] entities)
        {
            entities.Where(p=>!Exist(p)).ToList().ForEach(p => //主键不重复，可以插入
            {                
                List<XElement> elementList = new List<XElement>();
                properties.ForEach(s =>
                {
                    object content = s.GetValue(p,null);
                    string value = content != null ? content.ToString() : string.Empty;
                    elementList.Add(new XElement(GetXmlNodeName(s.Name), value));
                });
                XElement newObject = new XElement(GetXmlNodeName(type.Name), elementList.ToArray()); //新XML实体
                IEnumerable<XElement> users = doc.Descendants(GetXmlNodeName(type.Name));
                if (users.Count() != 0)
                {
                    users.Last().AddAfterSelf(newObject);
                }
                else //XML文件中没有任何实体
                {
                    doc.Descendants(tableName).Last().Add(newObject);
                }
            });            
        }

        
        /// <summary>
        /// 删除
        /// </summary>        
        /// <param name="Object">实体</param>
        public void Delete(Object Object)
        {
            Delete(new Object[] { Object });
        }

        /// <summary>
        /// 批量删除
        /// </summary>        
        /// <param name="entities">实体列表</param>
        public void Delete(Object[] entities)
        {
            if (entities.Length == 0) return;
            Assert(entities[0]);            
            entities.ToList().ForEach(p =>
            {
                XElement del = GetElement(p);
                if (del != null)
                {
                    del.Remove();
                }
            });         
        }

        /// <summary>
        /// 删除所有实体
        /// </summary>
        public void DeleteAll()
        {
            Assert();
            IEnumerable<XElement> table = doc.Descendants(tableName);
            if (table.Count() > 0)
            {
                table.Last().RemoveAll();
            }            
        }

        /// <summary>
        /// 更新
        /// </summary>        
        /// <param name="Object">实体</param>
        public void Update(Object Object)
        {
            Update(new Object[] { Object });
        }

        /// <summary>
        /// 批量更新
        /// </summary>        
        /// <param name="entities">实体列表</param>
        public void Update(Object[] entities)
        {
            if (entities.Length == 0) return;
            Assert(entities[0]);                              
            entities.ToList().ForEach(p => 
            {
                XElement del = GetElement(p);
                if (del != null)
                {
                    properties.ForEach(s =>
                    {
                        //逐个属性赋值，自动类型转换
                        object value = s.GetValue(p, null);
                        del.SetElementValue(GetXmlNodeName(s.Name), value == null ? "" : value);
                    });
                }
            });            
        }

        private string GetXmlNodeName(string name)
        {
            switch (XmlType)
            {
                case XMLType.Normal: return name;
                case XMLType.ToLower: return name.ToLower();
                case XMLType.ToUpper: return name.ToUpper();
                default: return name;
            }
        }
    }
}
